package org.fin.minicat.container;

import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Mapper {

  private Map<String, MappedHost> hosts;



  /* ----------------------------------------------------------------------------------------------- */
  // 向Mapper添加容器系列


  /**
   * 向Mapper添加一个host
   * @param hostname    要添加的hostname
   * @param host        对应的Host对象
   */
  public synchronized void addHost(String hostname, Host host) {
    if (hosts == null)
      hosts = new ConcurrentHashMap<>(4);
    if (hosts.containsKey(hostname))
      throw new RuntimeException("已经存在相同的host: " + hostname);


    MappedHost mappedHost = new MappedHost(host);
    hosts.put(hostname, mappedHost);
  }


  /**
   * 向指定的hostname下添加context，要求必须已经添加对应的host
   * @param hostname      要添加context对应的hostname
   * @param contextPath   要添加的context路径
   * @param context       对应的Context对象
   */
  public synchronized void addContext(String hostname, String contextPath, Context context) {
    if (!hosts.containsKey(hostname)) {
      throw new RuntimeException("host不存在，context添加失败: " + hostname);
    }

    MappedHost mappedHost = hosts.get(hostname);
    Map<String, MappedContext> mappedContexts = mappedHost.getSubElement();
    if (!mappedContexts.containsKey(contextPath)) {
      MappedContext mappedContext = new MappedContext(context);
      mappedContexts.put(contextPath, mappedContext);
    } else {
      throw new RuntimeException("在host: " + hostname + "下已经存在相同的context: " + contextPath);
    }
  }


  /**
   * 向指定的hostname下的context中添加Wrapper，要求必须已经添加对应的host和context
   * @param hostname            要添加wrapper的对应hostname
   * @param contextPath         要添加wrapper的对应context
   * @param wrapperUrlPattern   要添加wrapper的url路径
   * @param wrapper             对应的Wrapper对象
   */
  public void addWrapper(String hostname, String contextPath, String wrapperUrlPattern, Wrapper wrapper) {
    if (!hosts.containsKey(hostname)) {
      throw new RuntimeException("host不存在，wrapper添加失败: " + hostname);
    }

    MappedHost mappedHost = hosts.get(hostname);
    Map<String, MappedContext> mappedContexts = mappedHost.getSubElement();
    if (!mappedContexts.containsKey(contextPath)) {
      throw new RuntimeException("context不存在，wrapper添加失败: " + hostname);
    } else {
      MappedContext mappedContext = mappedContexts.get(contextPath);
      Map<String, MappedWrapper> wrappers = mappedContext.getSubElement();
      if (!wrappers.containsKey(wrapperUrlPattern)) {
        MappedWrapper mappedWrapper = new MappedWrapper(wrapper);
        wrappers.put(wrapperUrlPattern, mappedWrapper);
      } else {
        throw new RuntimeException("在host: " + hostname + "下的context: " + contextPath + "中已经存在相同的wrapper: " + wrapperUrlPattern);
      }
    }
  }




  /* ----------------------------------------------------------------------------------------------- */
  // 获取容器系列


  /**
   * 根据uri找到对应的Host返回
   * @param uri   请求uri
   * @return      返回对应的Host，如果找不到返回null
   */
  public Host findHost(URI uri) {
    MappedHost mappedHost = findMappedHost(uri);
    return mappedHost == null ? null : mappedHost.getElement();
  }


  /**
   * 根据uri找到对应的Host返回
   * @param uri   请求uri
   * @return      返回对应的Host，如果找不到返回null
   */
  public Context findContext(URI uri) {
    MappedHost mappedHost = findMappedHost(uri);
    if (mappedHost == null) {
      return null;
    }

    MappedContext mappedContext = findMappedContext(uri, mappedHost);
    return mappedContext == null ? null : mappedContext.getElement();
  }


  /**
   * 根据uri找到对应的Wrapper返回
   * @param uri 请求uri
   * @return  返回对应的Wrapper，如果找不到返回null
   */
  public Wrapper findWrapper(URI uri) {

    // 根据hostname找到对应的MappedHost
    MappedHost mappedHost = findMappedHost(uri);
    if (mappedHost == null) {
      return null;
    }

    // 根据contextPath获取对应的MappedContext
    MappedContext mappedContext = findMappedContext(uri, mappedHost);
    if (mappedContext == null) {
      return null;
    }

    // 获取定位Servlet用的wrapperUrlPattern
    String path = uri.getPath();
    String wrapperUrlPattern = path.substring(path.indexOf('/', 1));

    // 根据wrapperUrlPattern获取对应的MappedWrapper
    MappedWrapper mappedWrapper = mappedContext.getSubElement().get(wrapperUrlPattern);

    // 找到Wrapper就返回，否则返回null
    return mappedWrapper == null ? null : mappedWrapper.getElement();
  }




  /* ----------------------------------------------------------------------------------------------- */
  // 获取MapElement系列


  private MappedHost findMappedHost(URI uri) {
    String hostname = uri.getHost();
    // 找到Host就返回，否则返回null
    return hosts.get(hostname);
  }

  private MappedContext findMappedContext(URI uri, MappedHost mappedHost) {
    // 获取contextPath
    String path = uri.getPath();
    int contextPathSplitIndex = path.indexOf('/', 1);

    // 如果是-1，代表只带了context没有servlet或者静态资源的路径，直接返回null
    if (contextPathSplitIndex == -1) return null;
    String contextPath = path.substring(0, contextPathSplitIndex);

    // 找到Context就返回，否则返回null
    return mappedHost.getSubElement().get(contextPath);
  }




  /* ----------------------------------------------------------------------------------------------- */
  // 内部类系列


  protected abstract static class MapElement<E, S> {

    private final E element;

    private S subElement;

    public MapElement(E element) {
      this.element = element;
    }

    public abstract S createSubElement();

    public E getElement() {
      return element;
    }

    public S getSubElement() {
      synchronized (this) {
        if (subElement == null) {
          S s = createSubElement();
          setSubElement(s);
        }
      }
      return subElement;
    }

    public void setSubElement(S subElement) {
      this.subElement = subElement;
    }
  }

  protected static class MappedHost extends MapElement<Host, Map<String, MappedContext>> {

    public MappedHost(Host host) {
      super(host);
    }

    @Override
    public Map<String, MappedContext> createSubElement() {
      return new ConcurrentHashMap<>(8);
    }

  }

  protected static class MappedContext extends MapElement<Context, Map<String, MappedWrapper>> {

    private List<MappedWrapper> wrapperList = new LinkedList<>();

    public MappedContext(Context context) {
      super(context);
    }

    @Override
    public Map<String, MappedWrapper> createSubElement() {
      return new ConcurrentHashMap<>(16);
    }
  }

  protected static class MappedWrapper extends MapElement<Wrapper, Servlet> {

    public MappedWrapper(Wrapper wrapper) {
      super(wrapper);
    }

    @Override
    public Servlet createSubElement() {
      return null;
    }
  }

}
